home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1995 February: Tool Chest / Dev.CD Feb 95 / Dev.CD Feb 95.toast / Sample Code / Snippets / Memory / Switch Stack / Switch Stack.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-11-02  |  6.3 KB  |  192 lines  |  [TEXT/KAHL]

  1. /*
  2. **    File:        Switch Stack.c
  3. **
  4. **    Contains:    A simple example of a VBL written in THINK C that runs on a
  5. **                private stack.
  6. **
  7. **    Written by:    Jim Luther (Based on the VBL code from the Technical Note
  8. **                "MultiFinder Miscellanea".) 
  9. **
  10. **    Copyright:    © 1993 by Apple Computer, Inc., all rights reserved.\
  11. **
  12. **    Change History (most recent first):
  13. **
  14. **         <3>     11/02/93    JML        Added reentrancy comment to VBL
  15. **         <2>     10/13/93    JML        Minor cleanup
  16. **         <1>     10/08/93    JML        First pass
  17. **
  18. */
  19.  
  20. #include "Switch Stack.h"
  21.  
  22. /*----------------------------------------------------------------------------*/
  23.  
  24. /*
  25. **    A global which will be referenced from our VBL Task and the test program
  26. */
  27.  
  28. long    gCounter;    /* Counter incremented each time our VBL gets called */
  29.  
  30. /*----------------------------------------------------------------------------*/
  31.  
  32. /*
  33. **    GetVBLRec returns the address of the VBLRec associated with our VBL task.
  34. **    This works because on entry into the VBL task, A0 points to the theVBLTask
  35. **    field in the VBLRec record, which is the first field in the record and that
  36. **    is the address we return.  Note that this method works whether the VBLRec
  37. **    is allocated globally, in the heap (as long as the record is locked in 
  38. **    memory) or if it is allocated on the stack as is the case in this example.
  39. **    In the latter case this is OK as long as the procedure which installed the
  40. **    task does not exit while the task is running.  This trick allows us to get
  41. **    to the saved A5, but it could also be used to get to anything we wanted to
  42. **    store in the record.
  43. */
  44. VBLRecPtr GetVBLRec ()
  45.     = 0x2008;    /* MOVE.L    A0,D0 */
  46.  
  47. /*----------------------------------------------------------------------------*/
  48.  
  49. /*
  50. **    DoVBL is called only by StartVBL ()
  51. */
  52. void DoVBL (VBLRecPtr recPtr)
  53. {
  54.     gCounter++;                                /* Show we can set a global */
  55.     recPtr->theVBLTask.vblCount = INTERVAL;    /* Set ourselves to run again */
  56. }
  57.  
  58. /*----------------------------------------------------------------------------*/
  59.  
  60. /*
  61. **    This is the actual VBL task code.  It uses GetVBLRec to get our VBL record
  62. **    and properly set up A5.  Having done that, switches to a private stack and
  63. **    then  calls DoVBL to increment a global counter and sets itself to run again.
  64. **    Because of the vagaries of C optimization, it calls a separate routine to
  65. **    actually access global variables.  See M.OV.A5  - "Setting and Restoring A5"
  66. **    for the reasons for this, as well as for a description of SetA5.
  67. **    I can switch to my private stack without checking to make sure I haven't
  68. **    already done so because VBLs don't have to worry about begin reentrant.
  69. */
  70. void StartVBL ()
  71. {
  72.     long        curA5;
  73.     VBLRecPtr    recPtr;
  74.     
  75.     recPtr = GetVBLRec ();        /* First get our record */
  76.     curA5 = SetA5 (recPtr->VBLA5);    /* Get the saved A5 */
  77.     /* Now we can access globals */
  78.  
  79.     /* Switch to private stack */
  80.     asm
  81.     {
  82.         MOVEA.L    recPtr,A0                                ; A0 points to VBLRec
  83.         MOVE.L    HiHeapMark,VBLRec.savedHiHeapMark(A0)    ; save HiHeapMark
  84.         MOVE.L    StkLowPt,VBLRec.savedStkLowPt(A0)        ; save StkLowPt
  85.         MOVE.L    A7,VBLRec.savedA7(A0)                    ; save current system stack
  86.         CLR.L    StkLowPt                                ; disable the stack sniffer
  87.         MOVE.L    VBLRec.ourStackBottom(A0),HiHeapMark    ; set HiHeapMark to bottom of our stack
  88.         MOVEA.L    VBLRec.ourStackTop(A0),A7                ; switch stacks
  89.         MOVE.L    A0,-(SP)                                ; save A0 on top of private stack
  90.     }
  91.  
  92.     /*    Now we're running on our private stack */
  93.  
  94.     DoVBL(recPtr);            /* Call another routine to do actual work */
  95.  
  96.     /* Switch back to original stack */
  97.     asm
  98.     {
  99.         MOVE.L    (SP)+,A0                                ; restore A0 from private stack */
  100.         MOVEA.L    VBLRec.savedA7(A0),A7                    ; restore system stack
  101.         MOVE.L    VBLRec.savedHiHeapMark(A0),HiHeapMark    ; restore HiHeapMark
  102.         MOVE.L    VBLRec.savedStkLowPt(A0),StkLowPt        ; restore the sniffer
  103.     }
  104.  
  105.     (void) SetA5 (curA5);    /* Restore old A5 */
  106. }
  107.  
  108. /*----------------------------------------------------------------------------*/
  109.  
  110. /*
  111. **    Create a dialog just to demonstrate that the global variable
  112. **    is being updated by the VBL Task.  Before installing the VBL, we store
  113. **    our A5 in the actual VBL Task record, using SetCurrentA5 described in
  114. **    TM.OV.A5.  We also store the location of our private stack which the VBL code
  115. **    will run under. We'll run the VBL, showing the counter being incremented,
  116. **    until the mouse button is clicked.  Then we remove the VBL Task, close the
  117. **    dialog, and remove the mouse down events to prevent the application from
  118. **    being inadvertently switched by MultiFinder.
  119. */
  120. void main (void)
  121. {
  122.     VBLRec            theVBLRec;
  123.     DialogPtr        infoDPtr;
  124.     DialogRecord    infoDStorage;
  125.     Str255            numStr = "\pTest";
  126.     OSErr            theErr;
  127.     Handle            theItemHandle;
  128.     short            theItemType;
  129.     Rect            theRect;
  130.     long            lastCount = 0;
  131.  
  132.     MaxApplZone();
  133.     InitGraf((Ptr)&qd.thePort);
  134.     InitFonts();
  135.     InitWindows();
  136.     InitMenus();
  137.     TEInit();
  138.     InitDialogs(nil);
  139.     InitCursor();
  140.  
  141.     /* Store the current value of A5 in the VBLA5 field. */
  142.     theVBLRec.VBLA5 = SetCurrentA5 ();
  143.  
  144.     /* Allocate memory for our private stack and store it's location in ourStackBottom */
  145.     theVBLRec.ourStackBottom = NewPtrSys(kStackSize);
  146.     if (theVBLRec.ourStackBottom != nil)
  147.     {
  148.         /* Store location of our stack's top in ourStackTop */
  149.         theVBLRec.ourStackTop =
  150.             (Ptr)((unsigned long)theVBLRec.ourStackBottom + kStackSize);
  151.         
  152.         gCounter = 0;    /* Initialize our global counter */
  153.         
  154.         /* Put up the dialog */
  155.         infoDPtr = GetNewDialog (rInfoDialog, (Ptr) &infoDStorage, (WindowPtr) -1);
  156.         DrawDialog (infoDPtr);
  157.         GetDItem (infoDPtr, rStatTextItem, &theItemType, &theItemHandle, 
  158.             &theRect);
  159.         
  160.         /* Set the address of our routine */
  161.         theVBLRec.theVBLTask.vblAddr = (VBLProcPtr)StartVBL;
  162.         theVBLRec.theVBLTask.vblCount = INTERVAL;    /* Frequency of task, in ticks */
  163.         theVBLRec.theVBLTask.qType = vType;            /* qElement is a VBL task */
  164.         theVBLRec.theVBLTask.vblPhase = 0;
  165.         
  166.         /* Now install the VBL task */
  167.         theErr = VInstall((QElemPtr)&theVBLRec.theVBLTask);
  168.         
  169.         /* Display the counter until the mouse button is pushed */
  170.         if (!theErr)
  171.         {
  172.             do
  173.             {
  174.                 if (gCounter != lastCount)
  175.                 {
  176.                     lastCount = gCounter;
  177.                     NumToString(gCounter, numStr);
  178.                     SetIText(theItemHandle, numStr);
  179.                 }
  180.             } while (!Button ());
  181.             theErr = VRemove((QElemPtr)&theVBLRec.theVBLTask); /* Remove it when done */
  182.         }
  183.         
  184.         /* Finish up */
  185.         CloseDialog (infoDPtr);        /* Get rid of our dialog */
  186.         FlushEvents (mDownMask, 0);    /* Flush all mouse down events */
  187.         
  188.         /* Dispose of the private stack */
  189.         DisposePtr(theVBLRec.ourStackBottom);
  190.     }
  191. }
  192.